//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using OpenLS.Spreadsheet.Expressions;

namespace OpenLS.Spreadsheet.Parsing
{
    internal enum CellReferenceMode
    {
        Static,
        Dynamic,
    }

    internal class SingleWorksheetRangeFactory : RangeFactory
    {
        private readonly Worksheet _worksheet;
        private readonly CellReferenceMode _cellReferenceMode;

        public SingleWorksheetRangeFactory(Worksheet worksheet, Worksheet caller, CellReferenceMode cellReferenceMode)
            : base(caller)
        {
            Contract.Requires(worksheet != null);
            _worksheet = worksheet;
            _cellReferenceMode = cellReferenceMode;
        }


        private CellReferenceMode CellReferenceMode
        {
            get { return _cellReferenceMode; }
        }

        private DimensionCollection<Row> Rows
        {
            get { return Worksheet.Rows; }
        }

        private DimensionCollection<Column> Columns
        {
            get { return Worksheet.Columns; }
        }

        public override Worksheet Worksheet
        {
            get { return _worksheet; }
        }

        private CellReference getCellReferenceImp(CellReferenceToken cInfo)
        {
            Contract.Requires(cInfo != null);
            if (cInfo == null)
                return null;
            switch (CellReferenceMode)
            {
                case CellReferenceMode.Dynamic:
                    return new DynamicCellReference(new AnchoredRowReference(Rows[cInfo.Row], cInfo.LockRow),
                                                    new AnchoredColumnReference(Columns[cInfo.Column], cInfo.LockColumn));
                case CellReferenceMode.Static:
                    return new StaticCellReference(new AnchoredRowReference(Rows[cInfo.Row], cInfo.LockRow),
                                                    new AnchoredColumnReference(Columns[cInfo.Column], cInfo.LockColumn));
                    default: throw new NotSupportedException();
            }
        }


        internal override RangeReference GetRangeReference(ReferenceToken token)
        {
            if (token is ColumnReferenceToken)
            {
                var cr = (ColumnReferenceToken) token;
                return new FullColumnRangeReference(
                    new AnchoredColumnReference(Columns[cr.FirstIndex], cr.FirstlLocked),
                    new AnchoredColumnReference(Columns[cr.SecondIndex], cr.SecondLocked));
            }
            if (token is RowReferenceToken)
            {
                var cr = (RowReferenceToken) token;
                return new FullRowRangeReference(
                    new AnchoredRowReference(Rows[cr.FirstIndex], cr.FirstlLocked),
                    new AnchoredRowReference(Rows[cr.SecondIndex], cr.SecondLocked));
            }
            if (token is CellReferenceToken)
            {
                return new SingleCellRangeReference(GetCellReference((CellReferenceToken) token));
            }

            throw new NotImplementedException();
        }

        internal IRangeReferenceItem GetRangeReferenceItem(ReferenceToken token)
        {
            if (token is ColumnReferenceToken)
            {
                var cr = (ColumnReferenceToken) token;
                return new FullColumnRangeReference(
                    new AnchoredColumnReference(Columns[cr.FirstIndex], cr.FirstlLocked),
                    new AnchoredColumnReference(Columns[cr.SecondIndex], cr.SecondLocked));
            }
            if (token is RowReferenceToken)
            {
                var cr = (RowReferenceToken) token;
                return new FullRowRangeReference(
                    new AnchoredRowReference(Rows[cr.FirstIndex], cr.FirstlLocked),
                    new AnchoredRowReference(Rows[cr.SecondIndex], cr.SecondLocked));
            }
            if (token is CellReferenceToken)
            {
                return GetCellReference((CellReferenceToken) token);
            }

            throw new NotImplementedException();
        }

        protected override RangeReference GetRangeReferenceImp(RangeReferenceInfo rInfo)
        {
            return rInfo.Project(Worksheet);
        }

        public override Expression GetCellReferenceExpression(CellReferenceToken token)
        {
            return GetCellReferenceExpressionImp(token);
        }

        private CellEnumeratorExpression GetCellReferenceExpressionImp(CellReferenceToken token)
        {
            CellReference r = GetCellReference(token);
            return new CellReferenceExpression(r, Caller);
        }

        public override CellEnumeratorExpression GetAnyReferenceExpression(ReferenceToken token)
        {
            if (token is CellReferenceToken)
            {
                return GetCellReferenceExpressionImp((CellReferenceToken) token);
            }
            // if (token is RowReferenceToken)

            return new RangeReferenceExpression(GetRangeReference(token), Caller);
        }

        public override Expression GetRangeReferenceExpression(List<ReferenceToken> tokens)
        {
            if (tokens.Count == 1)
            {
                ReferenceToken token = tokens[0];
                if (token is CellReferenceToken)
                    return new CellReferenceExpression(GetCellReference((CellReferenceToken) token), Caller);
                return new RangeReferenceExpression(GetRangeReference(token), Caller);
            }
            if (Enumerable.Count(tokens, tt => tt is CellReferenceToken) == 2 && tokens.Count == 2)
            {
                var token1 = (CellReferenceToken) tokens[0];
                var token2 = (CellReferenceToken) tokens[1];
                return new RangeReferenceExpression(GetCellReference(token1), GetCellReference(token2), Caller);
            }
            IEnumerable<CellReference> cellExpressions = Enumerable.Select(tokens,
                                                                           token =>
                                                                           GetCellReference((CellReferenceToken) token));
            throw new NotSupportedException();
            //   return
            //     new RangeReferenceExpression(
            //       new ComplexRangeReference(Enumerable.Select(tokens, tt => getReference(tt))), Caller);
        }

        private IRangeReferenceItem getReference(ReferenceToken token)
        {
            return GetRangeReferenceItem(token);
        }

        public CellReference GetCellReference(CellReferenceToken cInfo)
        {
            if (cInfo == null)
                return null;
            return getCellReferenceImp(cInfo);
        }
    }
}